package cn.jasonone.tio.tcp.commons.impl;

import cn.hutool.core.lang.Assert;
import cn.jasonone.tio.tcp.commons.Command;
import cn.jasonone.tio.tcp.commons.CommandRegister;
import cn.jasonone.tio.tcp.commons.TcpConfiguration;
import cn.jasonone.tio.tcp.commons.CommandFactory;
import cn.jasonone.tio.tcp.handler.CommandHandler;
import cn.jasonone.tio.tcp.model.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.tio.core.ChannelContext;

import java.util.HashMap;
import java.util.Map;

/**
 * @author xujie
 * @version 1.0
 * @date 2022/1/19 8:14
 */
@Slf4j
@lombok.Data
public class SimpleCommandFactory implements CommandFactory, InitializingBean, ApplicationContextAware {
    /**
     * 指令缓存: Map<类型,Map<分组,Map<指令编码,指令对象>>>
     */
    private final Map<Byte, Map<Integer, Map<Integer, Command>>> commandCache = new HashMap<>();
    /**
     * 指令处理器缓存
     */
    private final Map<Command, CommandHandler> commandHandlerCache = new HashMap<>();
    @Setter
    private ApplicationContext applicationContext;

    @Override
    public boolean registerCommand(CommandHandler commandHandler, boolean replace) {
        Command command = commandHandler.getCommand();
        Assert.notNull(command, "指令不能为null");
        Assert.notNull(commandHandler, "指令处理器不能为null");
        if (commandHandlerCache.containsKey(command)) {
            if (replace) {
                // 注册指令处理器
                CommandHandler oldCommandHandler = commandHandlerCache.put(command, commandHandler);
                log.warn(MARKER, "[{}]的指令处理器已存在,将使用新的指令处理器[{}]替换旧的指令处理器[{}]", command, commandHandler, oldCommandHandler);
                return true;
            } else {
                log.error(MARKER, "指令处理器[{}]注册失败:[{}]的指令处理器已存在", commandHandler, command);
                return false;
            }
        } else {
            // 注册指令处理器
            commandHandlerCache.put(command, commandHandler);
            log.debug(MARKER, "注册指令处理器: [{}]->{}", command, commandHandler);
            // 添加指令缓存
            if (!commandCache.containsKey(command.getType())) {
                commandCache.put(command.getType(), new HashMap<>());
            }
            if (!commandCache.get(command.getType()).containsKey(command.getGroup())) {
                commandCache.get(command.getType()).put(command.getGroup(), new HashMap<>());
            }
            commandCache.get(command.getType()).get(command.getGroup()).put(command.getCode(), command);
            return true;
        }
    }

    @Override
    public Command getCommand(byte type, int group, int command) {
        if (commandCache.containsKey(type)) {
            if (commandCache.get(type).containsKey(group)) {
                return commandCache.get(type).get(group).get(command);
            }
        }
        return null;
    }


    @Override
    public CommandHandler getCommandHandler(Command command) {
        CommandHandler commandHandler = this.commandHandlerCache.get(command);
        Assert.notNull(commandHandler, "指令处理器不存在,请确认指令[{}]的处理器已正确注册", command);
        return commandHandler;
    }

    @Override
    public boolean hasCommandHandler(Command command) {
        return this.commandHandlerCache.containsKey(command);
    }

    @Override
    public void handler(Data data, ChannelContext channelContext) throws Exception {
        Command command = data.getCommand();
        CommandHandler commandHandler = getCommandHandler(command);
        commandHandler.handler(commandHandler.isPacket() ? data : data.getBody(), channelContext);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, CommandHandler> commandHandlerMap = applicationContext.getBeansOfType(CommandHandler.class);
        for (CommandHandler commandHandler : commandHandlerMap.values()) {
            this.registerCommand(commandHandler);
        }

        Map<String, TcpConfiguration> configurationMap = applicationContext.getBeansOfType(TcpConfiguration.class);
        for (TcpConfiguration configuration : configurationMap.values()) {
            configuration.registerCommandHandler(this);
        }
    }
}
